﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CSharpExperiments
{
	/// <summary>A Vector with typed length using subtyping.</summary>
	/// <remarks>These can just be renamed to "Vector*" if you're only going to use this implementation of vector.</remarks>
	public static class SVector
	{
		public static SVector0<T> Create<T>() { return new SVector0<T>(); }
		public static SVector1<T> Create<T>(T v1) { return new SVector1<T>(v1); }
		public static SVector2<T> Create<T>(T v1, T v2) { return new SVector2<T>(v1, v2); }
		public static SVector3<T> Create<T>(T v1, T v2, T v3) { return new SVector3<T>(v1, v2, v3); }
		public static SVector4<T> Create<T>(T v1, T v2, T v3, T v4) { return new SVector4<T>(v1, v2, v3, v4); }
		public static SVector5<T> Create<T>(T v1, T v2, T v3, T v4, T v5) { return new SVector5<T>(v1, v2, v3, v4, v5); }
		public static SVector6<T> Create<T>(T v1, T v2, T v3, T v4, T v5, T v6) { return new SVector6<T>(v1, v2, v3, v4, v5, v6); }
		public static SVector7<T> Create<T>(T v1, T v2, T v3, T v4, T v5, T v6, T v7) { return new SVector7<T>(v1, v2, v3, v4, v5, v6, v7); }
		public static SVector8<T> Create<T>(T v1, T v2, T v3, T v4, T v5, T v6, T v7, T v8) { return new SVector8<T>(v1, v2, v3, v4, v5, v6, v7, v8); }

		public static C Dot<A, B, C>(this SVector0<A> a, SVector0<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return default(C); }
		public static C Dot<A, B, C>(this SVector1<A> a, SVector1<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector2<A> a, SVector2<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector3<A> a, SVector3<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector4<A> a, SVector4<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector5<A> a, SVector5<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector6<A> a, SVector6<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector7<A> a, SVector7<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }
		public static C Dot<A, B, C>(this SVector8<A> a, SVector8<B> b, Func<A, B, C> multiply, Func<C, C, C> add) { return a.Values().Zip(b.Values(), multiply).Aggregate(add); }

		public static double Dot(this SVector0<double> x, SVector0<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector1<double> x, SVector1<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector2<double> x, SVector2<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector3<double> x, SVector3<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector4<double> x, SVector4<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector5<double> x, SVector5<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector6<double> x, SVector6<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector7<double> x, SVector7<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }
		public static double Dot(this SVector8<double> x, SVector8<double> y) { return x.Dot(y, (i, j) => i * j, (m, n) => m + n); }

		public static SVector3<C> Cross<A, B, C>(this SVector1<A> a, SVector1<B> b, Func<A, B, C> multiply, Func<C, C, C> add, Func<C, C> negate, C zero)
		{
			return SVector.Create(zero, zero, zero);
		}

		public static SVector3<C> Cross<A, B, C>(this SVector2<A> a, SVector2<B> b, Func<A, B, C> multiply, Func<C, C, C> add, Func<C, C> negate, C zero)
		{
			return SVector.Create(zero, zero, add(multiply(a._1, b._2), negate(multiply(a._2, b._1))));
		}

		public static SVector3<C> Cross<A, B, C>(this SVector3<A> a, SVector3<B> b, Func<A, B, C> multiply, Func<C, C, C> add, Func<C, C> negate, C zero)
		{
			return SVector.Create(
				add(multiply(a._2, b._3), negate(multiply(a._3, b._2))),
				add(multiply(a._3, b._1), negate(multiply(a._1, b._3))),
				add(multiply(a._1, b._2), negate(multiply(a._2, b._1))));
		}

		public static SVector3<double> Cross(this SVector1<double> x, SVector1<double> y) { return x.Cross(y, (i, j) => i * j, (i, j) => i + j, i => -i, 0.0); }
		public static SVector3<double> Cross(this SVector2<double> x, SVector2<double> y) { return x.Cross(y, (i, j) => i * j, (i, j) => i + j, i => -i, 0.0); }
		public static SVector3<double> Cross(this SVector3<double> x, SVector3<double> y) { return x.Cross(y, (i, j) => i * j, (i, j) => i + j, i => -i, 0.0); }
	}

	public class SVector0<T>
	{
		public int Degree { get { return 0; } }

		public IEnumerable<T> Values()
		{
			yield break;
		}

		protected internal SVector0()
		{
		}

		public bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector0<T>))
				return false;
			var that = (SVector0<T>) other;
			return true;
		}
	}

	public class SVector1<T> : SVector0<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public virtual T _1 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector1(T v1) : base()
		{
			value = v1;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector1<T>))
				return false;
			var that = (SVector1<T>) other;
			return Object.Equals(this._1, that._1);
		}
	}

	public class SVector2<T> : SVector1<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _2 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector2(T v1, T v2) : base(v1)
		{
			value = v2;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector2<T>))
				return false;
			var that = (SVector2<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2);
		}
	}

	public class SVector3<T> : SVector2<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _3 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector3(T v1, T v2, T v3) : base(v1, v2)
		{
			value = v3;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector3<T>))
				return false;
			var that = (SVector3<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2)
				&& Object.Equals(this._3, that._3);
		}
	}

	public class SVector4<T> : SVector3<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _4 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector4(T v1, T v2, T v3, T v4) : base(v1, v2, v3)
		{
			value = v4;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector4<T>))
				return false;
			var that = (SVector4<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2)
				&& Object.Equals(this._3, that._3)
				&& Object.Equals(this._4, that._4);
		}
	}

	public class SVector5<T> : SVector4<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _5 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector5(T v1, T v2, T v3, T v4, T v5) : base(v1, v2, v3, v4)
		{
			value = v5;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector5<T>))
				return false;
			var that = (SVector5<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2)
				&& Object.Equals(this._3, that._3)
				&& Object.Equals(this._4, that._4)
				&& Object.Equals(this._5, that._5);
		}
	}

	public class SVector6<T> : SVector5<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _6 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector6(T v1, T v2, T v3, T v4, T v5, T v6) : base(v1, v2, v3, v4, v5)
		{
			value = v6;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector6<T>))
				return false;
			var that = (SVector6<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2)
				&& Object.Equals(this._3, that._3)
				&& Object.Equals(this._4, that._4)
				&& Object.Equals(this._5, that._5)
				&& Object.Equals(this._6, that._6);
		}
	}

	public class SVector7<T> : SVector6<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _7 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector7(T v1, T v2, T v3, T v4, T v5, T v6, T v7) : base(v1, v2, v3, v4, v5, v6)
		{
			value = v7;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector7<T>))
				return false;
			var that = (SVector7<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2)
				&& Object.Equals(this._3, that._3)
				&& Object.Equals(this._4, that._4)
				&& Object.Equals(this._5, that._5)
				&& Object.Equals(this._6, that._6)
				&& Object.Equals(this._7, that._7);
		}
	}

	public class SVector8<T> : SVector7<T>
	{
		public new int Degree { get { return base.Degree + 1; } }
		public T _8 { get { return value; } }

		public new IEnumerable<T> Values()
		{
			foreach (T x in base.Values()) yield return x;
			yield return value;
		}

		private T value;

		protected internal SVector8(T v1, T v2, T v3, T v4, T v5, T v6, T v7, T v8) : base(v1, v2, v3, v4, v5, v6, v7)
		{
			value = v8;
		}

		public new bool SameAs(Object other)
		{
			if (other == null || ! (other is SVector8<T>))
				return false;
			var that = (SVector8<T>) other;
			return Object.Equals(this._1, that._1)
				&& Object.Equals(this._2, that._2)
				&& Object.Equals(this._3, that._3)
				&& Object.Equals(this._4, that._4)
				&& Object.Equals(this._5, that._5)
				&& Object.Equals(this._6, that._6)
				&& Object.Equals(this._7, that._7)
				&& Object.Equals(this._8, that._8);
		}
	}
}
